/* 
 * File:   DataSet.cpp
 * Author: André
 * 
 * Created on December 4, 2012, 3:19 PM
 */

#include "DataSet.h"
#include "Connection.h"
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <fstream>

#define DEBUG

using namespace std;

DataSet :: DataSet(string fname, int k)
{
    numEntries = 0;
                                                                        
    ifstream parametersFile(fname.c_str());
    
    string temp;
    string dataFileName;
    string numNeuronsPerLayer;

    getline(parametersFile,dataFileName);                                       //obtain filename that contains data
    getline(parametersFile,temp);                                               //obtain number of hidden layers
    
    numHiddenLayers = atoi( temp.c_str());                                      // convert to integer
    
    getline(parametersFile,numNeuronsPerLayer);                                 //obtain number of neurons per hidden layer
    
    numberOfHiddenNeuronsPerLayer(numNeuronsPerLayer);                          // parse line containing number of neurons per hidden layer
        
    numBins = k;                                                                // set numBins = k (if 1, training set = validation set)
                                                                                // else, this specifies in how many parts the DataSet will be split
    getline(parametersFile, temp);
    learningRate = atof(temp.c_str());                                          // read learning rate
    
    getline(parametersFile, temp);
    errorTol = atof(temp.c_str());                                              // read error tolerance
       
    parametersFile.close();                                                     // close the file

    ifstream dataFile(dataFileName.c_str());                                    // open file containing the data itself
    
    while (getline(dataFile,temp))                                              // obtain total number of entries
        numEntries++;
    
    numEntries -= 2;                                                            // subtract two lines (first is scaling factor, second is number of i/o's)
    
    dataFile.seekg(0, ios::beg);                                                // return pointer to beginning
    dataFile.close();                                                           // close data file
   
    buildDataSet(dataFileName);                                                 // run function to extract data points
   
}

DataSet::~DataSet() {
}

void DataSet :: numberOfHiddenNeuronsPerLayer(string numNeuronsPerLayer)
{
    string temp;
    stringstream stream(numNeuronsPerLayer);
    
    numHiddenNeuronsPerLayer = new int[numHiddenLayers];                            // create array to store 
    
    int i = 0;
    
    while (getline(stream, temp, ','))
    {
        numHiddenNeuronsPerLayer[i] = atoi(temp.c_str());
        i++;
    }
    
}

void DataSet :: buildDataSet(string dataFileName)
{
    ifstream dataFile(dataFileName.c_str());                                    // open data file
    string temp, inputAndOutput, currentTuple;                                  // auxiliary variables
    int j = 0;
    
    getline(dataFile, temp);                                                    // obtain line from file
    scalingFactor = atof(temp.c_str());                                         // parse scaling factor
    
    temp.clear();
    
    getline(dataFile, inputAndOutput);
    stringstream stream(inputAndOutput);
    
    while (getline(stream, temp, ','))
    {
        if (j == 0)
            numInputs = atoi( temp.c_str());
        if (j == 1)
            numOutputs = atoi( temp.c_str());

        j++;
    }
    
    buildDataSetArrays();                                                       // allocate memory for values 
     
    int inputIterator = 0;
    int outputIterator = 0;

    for (int bin = 0; bin < numBins; bin++)                                        // obtains example from file and dumps it in memory         
    {
        for (int entry = 0; entry < getNumItemsPerBin(); entry++)
        {
            getline(dataFile,currentTuple);                                         // get line from file
            stringstream ss(currentTuple);

            while (getline(ss,temp, ','))                                           // while line is not over
            {      
                    if (inputIterator < numInputs)                                  // go through all inputs
                      {
                       inputBinsAndEntries[bin][entry][inputIterator] = atof( temp.c_str());        // store in dataSetInput array
                       inputIterator++;
                      }
                    else
                     if (outputIterator < numOutputs)                               // go through all outputs
                      {
                       outputBinsAndEntries[bin][entry][outputIterator] = atof( temp.c_str());      // store in dataSetOutput array
                       outputIterator++;
                       }

             }
        
        inputIterator = 0;                                                      // reset input counter
        outputIterator = 0;                                                     // reset output counter   
        }
    }
        
    

#ifdef DEBUG 
    cout << "--- DATA SET PARAMETERS ---" << endl;
    cout << "Inputs: " << numInputs << " Outputs: " << numOutputs << endl;
    cout << "Scaling factor: " << scalingFactor << endl;
    cout << "Number of records: " << numEntries << endl;
    
    cout << endl;
    
    cout << "--- PROGRAM PARAMETERS ---" << endl;
    cout << "Hidden Layers: " << numHiddenLayers << " Neurons per h. layer:";
    
    for (int i = 0; i < numHiddenLayers; i++)
        cout << " " << numHiddenNeuronsPerLayer[i];
    cout << endl;
    cout << "Learning rate: " << learningRate << endl;
    cout << "Error tol.: " << errorTol << endl;
    cout << "Number of bins (k): " << numBins << endl;
    
    cout << endl;
    
    cout << "--- DATA SET ENTRIES ---" << endl;
    
    j = 0;
    while (j < numEntries)                                                      // loop over all entries (this is a while loop)
    {
        for (int bin = 0; bin < numBins; bin++)                                 // loop over all bins
        {
            cout << "BIN #" << bin + 1 << endl;                                 // print index of current bin (1 through k)

            for (int entry = 0; entry < getNumItemsPerBin(); entry++)           // loop over all elements in a bin
            {
                cout << "ENTRY #" << entry+1 << " (" << j+1 << ") Input(s):";   // print index of current entry in bin (1 through numItemsPerBin)

                for (int c = 0; c < numInputs; c++)                         
                    cout << " " << inputBinsAndEntries[bin][entry][c];          // print input value at index c

                cout << "\n   Output(s):";

                for (int d = 0; d < numOutputs; d++)
                    cout << " " << outputBinsAndEntries[bin][entry][d];         // print output value at index d

                j = j + 1;                                                      // go to next entry 
                cout << endl;
            }                                                                   // finish looping over all items in bin

            cout << endl;

        }                                                                       // finish looping over all bins   
     }                                                                          // finish looping over all entries 
#endif
    
}

double* DataSet :: getInputEntryFromBin(int bin, int entry)
{ return inputBinsAndEntries[bin][entry]; }

double* DataSet :: getOutputEntryFromBin(int bin, int entry)
{ return outputBinsAndEntries[bin][entry]; }

int DataSet :: getNumInputs()
{ return numInputs; }

int DataSet :: getNumOutputs()
{ return numOutputs; }

void DataSet :: buildDataSetArrays()
{
    inputBinsAndEntries = new double**[numBins];                                // create input bin arrays
    outputBinsAndEntries = new double**[numBins];                               // create output bin arrays

    for (int i = 0; i < numBins; i++)
    {
        inputBinsAndEntries[i] = new double*[getNumItemsPerBin()];              // make every bin store same number of items
        outputBinsAndEntries[i] = new double*[getNumItemsPerBin()];
    }
    
    for (int bin = 0; bin < numBins; bin++)                                     // for every bin
        for (int entry = 0 ; entry < getNumItemsPerBin(); entry++)              // for every item in a bin
        {
                inputBinsAndEntries[bin][entry] = new double[numInputs];        // allocate array size = numInputs
                outputBinsAndEntries[bin][entry] = new double[numOutputs];      // allocate array size = numOutputs
        }
}

double DataSet :: getErrorTolerance()
{ return errorTol; }

int DataSet :: getNumHiddenLayers()
{ return numHiddenLayers; }

int* DataSet :: getNumOfNeuronsPerHiddenLayer()
{ return numHiddenNeuronsPerLayer; }

int DataSet :: getNumEntries()
{ return numEntries; }

double DataSet :: getScalingFactor()
{ return scalingFactor; }

double DataSet :: getLearningRate()
{ return learningRate; }

int DataSet :: getNumBins()
{ return numBins; }

int DataSet :: getNumItemsPerBin()
{ return numEntries/numBins; }

void DataSet :: discretizeAttribute(int index)
{
    double maxValue = 0.0;
    double minValue = 9999.0;
    double discretizedValue = 0;
    
    for (int i = 0; i < numBins; i++)
        for (int j = 0; j < getNumItemsPerBin(); j++)
        {
            if (getInputEntryFromBin(i,j)[index] > maxValue)
                maxValue = getInputEntryFromBin(i,j)[index];
            
            if (getInputEntryFromBin(i,j)[index] < minValue)
                minValue = getInputEntryFromBin(i,j)[index];
        }
    
    cout << "max value is " << maxValue << endl;
    cout << "min value is " << minValue << endl;
    
    for (int i = 0; i < numBins; i++)
        for (int j = 0; j < getNumItemsPerBin(); j++)
        {
            discretizedValue = (getInputEntryFromBin(i,j)[index] - minValue);
            discretizedValue /= (maxValue - minValue);
            cout << discretizedValue << endl;
        }   
}

void DataSet :: averageAttribute(int index)
{
    double sum = 0;
    double numNonNulls = 0;
    
    for (int i = 0; i < numBins; i++)
        for (int j = 0; j < getNumItemsPerBin(); j++)
        {
            if (getInputEntryFromBin(i,j)[index] != 0)
            {
                sum += getInputEntryFromBin(i,j)[index];
                numNonNulls++;
                cout << "non null value of " << getInputEntryFromBin(i,j)[index] << endl;
            }
        }
    double avg = sum / numNonNulls;
    cout << numNonNulls << "non null entries found" << endl;
    
    for (int i = 0; i < numBins; i++)
        for (int j = 0; j < getNumItemsPerBin(); j++)
        {
            if(getInputEntryFromBin(i,j)[index] == 0)
                cout << avg << endl;
            else
                cout << getInputEntryFromBin(i,j)[index] << endl;
            
        }
    
    
    cout << "accumulator totaled " << sum << " for an average value of " << avg << endl;
    
    
}

void DataSet :: printWekaFormat()
{
    for (int bin = 0; bin < numBins; bin++)
    {
        for (int entry = 0; entry < getNumItemsPerBin(); entry++)
        {
            for (int i = 0; i < numInputs; i++)
                cout << getInputEntryFromBin(bin, entry)[i] << ",";
                
                for (int j = 0; j < numOutputs; j++)
                    if ( getOutputEntryFromBin(bin, entry)[j] == 0)
                        cout << "false" << endl;
                    else
                        cout << "true" << endl;
            
        }
    }
      
}